const Response = require("../../class/Response");
WSC.get(/\/bds\/sync\/(.*)/, (wsclient, req, res) => {
    if (!CONF.CONFIG.get("SyncConfig")["Enabled"]) {
        return false; // 未开启同步
    }
});

const {getPlayerData, setPlayerData} = require('./sync.js');


// ================================================
// 一般同步服务
{   
    // 强制同步本服玩家到主服
    WSC.get('/bds/sync/syncPlayerData/checkAndForceSync', async (wsclient, req, res)=>{
        let {statusCode, statusMessage} = await syncPlayerData({
            xuid: req.data.xuid,
            uuid: req.data.uuid,
            realName: req.data.realName
        }, {
            ...req.data,
            xuid: undefined,
            uuid: undefined,
            realName: undefined
        });

        WSC.client.sendJson(Response.replyTo(req)
            .setUrl("/bds/sync/syncPlayerData/checkAndForceSync/result")
            .setStatusCode(statusCode)
            .setStatusMessage(statusMessage)
            .setData({
                ...req.data
            })
        );
    });

    // 监听命令发送同步请求(仅主服执行命令才会同步到其他服)
    mc.listen("onConsoleCmd",(command)=>{
        if (!CONF.CONFIG.get('SyncConfig')['Enabled']){
            return false;
        }

        if (CONF.CONFIG.get('SyncConfig')['SyncType'] == 'main'){
            let cmdList = command.split(' ');
            if (CONF.CONFIG.get("SyncConfig")["SyncSendOption"].syncCmd.includes(cmdList[0])){
                WSC.client.sendJson(
                    new Response()
                    .setUrl("/bds/sync/syncCmd")
                    .setData({
                        commandLine: command
                    }
                ));
            }
        }
    });

    // 执行命令同步请求
    WSC.get('/bds/sync/executeCmd', (wsclient, req, res)=>{
        if (req.body.statusCode != 0){return false;}
        let cmdList = req.body.data.commandLine.split(' ');
        if (CONF.CONFIG.get("SyncConfig")["SyncReceiveOption"].syncCmd.includes(cmdList[0])){
            let cmdRes = mc.runcmdEx(req.body.data.commandLine);
            WSC.logger.info(`[同步命令] ${req.body.data.commandLine} | Success: ${cmdRes.success} | Output: ${cmdRes.output}`);
        }
    });
}


// ================================================
// 主服服务项
{
    // 服务项 - [作为主服] - 登录同步获取玩家数据
    WSC.get('/bds/sync/loginSync/getPlayerData/requestMainBDS', (wsclient, req, res)=>{
        if (req.body.statusCode != 0){return;}
        res = Response.replyTo(req)
            .setUrl("/bds/sync/loginSync/getPlayerData/requestMainBDS/result");
        // 获取玩家数据
        let playerData = getPlayerData(req.body.data);
        if (playerData == null){
            res.setStatusCode(1);
            res.setStatusMessage('主服无此玩家数据');
            res.setData(req.body.data);
            WSC.logger.warn('获取玩家数据失败 ' +  req.body.data.realName);
        }else{
            res.setData({
                playerData: playerData,
                playerSyncTemp: req.body.data.playerSyncTemp,
                onlineClientsInfo: req.body.data.onlineClientsInfo,
                xuid: req.body.data.xuid,
                uuid: req.body.data.uuid,
                realName: req.body.data.realName
            });
        }    
        WSC.client.sendJson(res.json);
        return false;
    });

    // 服务项 - [作为主服] - 保存玩家数据
    WSC.get('/bds/sync/syncPlayerData/requestMainBDS', (wsclient, req, res)=>{
        if (req.body.statusCode != 0){return;}
        res = Response.replyTo(req)
            .setUrl("/bds/sync/syncPlayerData/requestMainBDS/result")
            .setData({
                xuid: req.body.data.xuid,
                uuid: req.body.data.uuid,
                realName: req.body.data.realName,
                ...req.body.data
            });

        // 保存玩家数据
        let {success, statusMessage} = setPlayerData(req.body.data, req.body.data.playerData);
        if (success){
            res.setStatusCode(0);
            res.setStatusMessage('保存玩家数据成功');
            delete res.body.data['playerData'];
        }else{
            res.setStatusCode(1);
            res.setStatusMessage('保存玩家数据失败 '+ statusMessage);
            WSC.logger.error('保存玩家数据失败 ' +  req.body.data.realName);
        }
        WSC.client.sendJson(res.json);
        return false;
    });
}


// ================================================
//LLMoney同步服务
{
    // 服务项 - [作为主服] - 获取所有玩家llmoney信息
    WSC.get('/bds/sync/getAllPlayersLLMoney/requestMainBDS', (wsclient, req, res)=>{
        WSC.client.sendJson(Response.replyTo(req)
            .setUrl("/bds/sync/getAllPlayersLLMoney/requestMainBDS/result")
            .setData({
                allLLMoney: Object.fromEntries(req.data.xuids.map(xuid=>[xuid, money.get(xuid)]))
            })
        );
    });

    // 服务项 - LLMoney 实时同步
    WSC.get('/bds/sync/setllmoney', (wsclient, req, res)=>{
        if (!CONF.CONFIG.get('SyncConfig')['SyncReceiveOption']['syncLLMoney']){return;}
        if (money.get(req.data.xuid) != req.data.value){
            WSC.client.syncLLMoneyStatus.add(req.data.xuid);
            money.set(req.data.xuid, req.data.value);
            WSC.client.syncLLMoneyStatus.delete(req.data.xuid);
        }
        logger.debug(`[Sync] 同步玩家(${req.data.xuid})LLmoney: ${req.data.value}`);
    });

    // 应用所有玩家LLMoney数据
    WSC.get('/bds/sync/getAllPlayersLLMoney/result', (wsclient, req, res)=>{
        if (!CONF.CONFIG.get('SyncConfig')['SyncReceiveOption']['syncLLMoney']){return;}
        if (req.body.statusCode != 0){
            WSC.logger.warn(req.body.statusMessage);
            return;
        }
        for (xuid in req.body.data.allLLMoney){
            if (money.get(xuid) != req.body.data.allLLMoney[xuid]){
                WSC.client.syncLLMoneyStatus.add(req.data.xuid);
                money.set(xuid, req.body.data.allLLMoney[xuid]);
                WSC.client.syncLLMoneyStatus.delete(req.data.xuid);
            }
        }
        WSC.logger.info(`已更新所有玩家最新LLmoney`);
    });

    mc.listen("onMoneyAdd", (xuid, num)=>{
        if (!CONF.CONFIG.get('SyncConfig')['Enabled']){return;}
        if (!CONF.CONFIG.get('SyncConfig')['SyncSendOption']['syncLLMoney']){return;}
        if (WSC.client.syncLLMoneyStatus.has(xuid)){return;} 
        WSC.client.sendJson(new Response()
            .setUrl("/bds/sync/syncllmoney")
            .setData({
                xuid: xuid,
                value: money.get(xuid),
            })
        );
    });

    mc.listen("onMoneyReduce", (xuid, num)=>{
        if (!CONF.CONFIG.get('SyncConfig')['Enabled']){return;}
        if (!CONF.CONFIG.get('SyncConfig')['SyncSendOption']['syncLLMoney']){return;}
        if (WSC.client.syncLLMoneyStatus.has(xuid)){return;} 
        WSC.client.sendJson(new Response()
            .setUrl("/bds/sync/syncllmoney")
            .setData({
                xuid: xuid,
                value: money.get(xuid),
            })
        );
    });

    mc.listen("onMoneySet", (xuid, num)=>{
        if (!CONF.CONFIG.get('SyncConfig')['Enabled']){return;}
        if (!CONF.CONFIG.get('SyncConfig')['SyncSendOption']['syncLLMoney']){return;}
        if (WSC.client.syncLLMoneyStatus.has(xuid)){return;} 
        WSC.client.sendJson(new Response()
            .setUrl("/bds/sync/syncllmoney")
            .setData({
                xuid: xuid,
                value: money.get(xuid),
            })
        );
    });
}


// ================================================
// 意外情况处理
{
    // 30s在线玩家自动同步
    if (CONF.CONFIG.get('SyncConfig')['Enabled']){
        if (CONF.CONFIG.get('SyncConfig')['SyncType'] == 'sub'){
            setInterval(async ()=>{
                try{
                    for (let pl of mc.getOnlinePlayers()){
                        if (pl.isSimulatedPlayer()){return;}
                        if (WSC.client.playerLoginSyncSuccess[pl.uuid]){
                            syncPlayerData({
                                xuid: pl.xuid,
                                uuid: pl.uuid,
                                realName: pl.realName
                            });
                            await sleep(150);
                        }
                    }
                }catch(err){
                    WSC.logger.error('自动同步玩家数据失败: ', err);
                }
            }, CONF.CONFIG.get('SyncConfig')['AutoSyncCycle']);
        }
    }
    async function sleep(ms){
        return new Promise(resolve=>{
            setTimeout(resolve, ms);
        });
    }

    // 玩家退出，若是副服则执行一次同步数据
    WSC.get('/bds/playerLogout/result', (wsclient, req, res)=>{
        if (!CONF.CONFIG.get('SyncConfig')['Enabled']){return;}
        // 如果登录同步成功，则退出同步一次数据，并通知WS服务端玩家退出状态改为正常退出
        if (WSC.client.playerLoginSyncSuccess[req.data.uuid]){
            if (CONF.CONFIG.get('SyncConfig')['SyncType'] == 'sub'){
                syncPlayerData(req.data);
            }
            WSC.client.sendJson(new Response()
                .setUrl("/bds/sync/playerLogout")
                .setData({
                    xuid: req.body.data.xuid,
                    uuid: req.body.data.uuid,
                    realName: req.body.data.realName,
                })
            );
        }
        // 清除登录同步成功状态
        delete WSC.client.playerLoginSyncSuccess[req.data.uuid];
        return false;
    });

    // WS连接断开状态下则禁止玩家登录
    mc.listen("onPreJoin", (pl)=>{
        if (!CONF.CONFIG.get('SyncConfig')['Enabled']){return;}
        if (pl.isSimulatedPlayer()){return;}
        if (WSC == null || WSC.ws == null || WSC.ws.readyState != 1){
            disconnectPlayer({xuid:pl.xuid,uuid:pl.uuid,realName:pl.realName}, `BDS已断开协调服务端连接, 为防止失去同步, 本服禁止进入`);
            return;
        }
    });

    // 主服连接到WS服务端
    WSC.get(CONSTANTS.EVENT.CLIENT_CONNECT, (wsclient, req, res)=>{
        if (req.data.userType != 'bds'){return;}
        if (!CONF.CONFIG.get('SyncConfig')['Enabled']){return;}
        if (req.data?.bds?.syncType == 'main' && CONF.CONFIG.get('SyncConfig')['SyncType'] == 'sub'){
            // 副服更新所有玩家最新llmoney信息
            if (CONF.CONFIG.get('SyncConfig')['SyncReceiveOption']['syncLLMoney']){
                res.setUrl("/bds/sync/getAllPlayersLLMoney");
                res.setSourceBdsUuid(CONF.CONFIG.get('Websocket')['BDSUuid']);
                res.setSourceBdsServerName(CONF.CONFIG.get('Websocket')['ServerName']);
                res.setData({});
                WSC.client.sendJson(res.json);
            }

            // 若本服是主服则无需进入玩家重连流程, 若本服是副服则服内必没有玩家在线
            mc.getOnlinePlayers().forEach(pl => {
                if (pl.isSimulatedPlayer()){return;}
                WSC.logger.error(`断言错误: 主服连接到WS服务端, 本服此时应没有玩家在线`);
            });
        }
    });

    // 主服断开WS服务端
    WSC.get(CONSTANTS.EVENT.CLIENT_CLOSE, (wsclient, req, res)=>{
        if (!CONF.CONFIG.get('SyncConfig')['Enabled']){return;}
        if (req.data.userType == 'player'){return;}
        if (req.data?.bds?.syncType == 'main'){
            mc.getOnlinePlayers().forEach(pl => {
                if (pl.isSimulatedPlayer()){return;}
                disconnectPlayer({xuid:pl.xuid,uuid:pl.uuid,realName:pl.realName}, `主服已断开WS协调服务端连接, 为防止失去同步, 本服禁止进入`);
            });
            // 初始化状态信息
            WSC.client.playerLoginSyncSuccess = {};
            WSC.client.syncLLMoneyStatus = new Map();
        }
    });

    // WS连接后在线玩家重连
    WSC.get('/bds/init/result', (wsclient, req, res)=>{
        if (!CONF.CONFIG.get('SyncConfig')['Enabled']){return;}
        // WS连接后在线的玩家会重登记到WS服务端，然后按正常流程触发重连登录同步

        // WS连接后副服更新所有玩家最新llmoney信息
        if (CONF.CONFIG.get('SyncConfig')['SyncReceiveOption']['syncLLMoney']){
            if (CONF.CONFIG.get('SyncConfig')['SyncType'] == 'sub'){
                res.setUrl("/bds/sync/getAllPlayersLLMoney");
                res.setSourceBdsUuid(CONF.CONFIG.get('Websocket')['BDSUuid']);
                res.setSourceBdsServerName(CONF.CONFIG.get('Websocket')['ServerName']);
                res.setData({});
                WSC.client.sendJson(res.json);
            }
        }
    });

    // WS连接关闭后client对象丢失，playerStatus信息也丢失, 需要断开玩家连接防止失去同步, 主服当前在线玩家可以继续玩
    WSC.on('close', (code, reason)=>{
        if (!CONF.CONFIG.get('SyncConfig')['Enabled']){return;}
        if (CONF.CONFIG.get('SyncConfig')['SyncType'] == 'sub'){
            mc.getOnlinePlayers().forEach(pl => {
                if (pl.isSimulatedPlayer()){return;}
                disconnectPlayer({xuid:pl.xuid,uuid:pl.uuid,realName:pl.realName}, `本服已断开WS协调服务端连接, 为防止失去同步, 本服禁止进入`);
            });
            // 初始化状态信息
            WSC.client.playerLoginSyncSuccess = {};
            WSC.client.syncLLMoneyStatus = new Map();
        }
    });
}



// ================================================
// 同步一次玩家数据(otherData做一些同步时额外的配置, 比如lastLogoutSuccess的配置，用来检查意外退出时的修复同步)
async function syncPlayerData(playerInfo, otherData){
    // 获取玩家数据
    let playerData = getPlayerData(playerInfo);
    if (playerData == null){
        WSC.logger.error('获取玩家数据失败 ' +  playerInfo.realName);
        return {statusCode: 1, statusMessage: '获取玩家数据失败', ...playerInfo};
    }

    let {req, res} = await WSC.client.sendJsonAsync(new Response()
        .setSourceId(WSC.client.info.id)
        .setSourceBdsUuid(CONF.CONFIG.get('Websocket')['BDSUuid']) // 源BDSuuid, 涉及到客户端间通信时主动设置
        .setSourceBdsServerName(CONF.CONFIG.get('Websocket')['ServerName'])
        .setUrl("/bds/sync/syncPlayerData")
        .setData({
            ...otherData,
            playerData: playerData,
            xuid: playerInfo.xuid,
            uuid: playerInfo.uuid,
            realName: playerInfo.realName
        })
    );

    if (req?.body?.statusCode != 0){
        WSC.logger.error(`[syncPlayerData] ${playerInfo.realName} 未能同步该玩家数据到主服`);
        return {statusCode: 1, statusMessage: req?.body?.statusMessage??'', ...playerInfo};
    }
    return {statusCode: 0, statusMessage: req.body.statusMessage, ...playerInfo};
}

function disconnectPlayer(playerInfo, reason){
    if (playerInfo?.xuid == null){return false;}
    let pl = mc.getPlayer(playerInfo.xuid);
    if (pl == null){return false;}
    pl.disconnect(reason);
    WSC.logger.warn(`[同步失败] ${playerInfo.realName} ${reason}`);
    WSC.client?.sendServerLogger(`[同步失败] ${playerInfo.realName} ${reason}`, 'warn');
    return null;
}